using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Logging
{
    public class Logger
    {

        public static Logger Global = new Logger("",null);

        private string name;
        private string resourceBundleName;

        private List<Handler> handlers;

        private Filter filter;
        private Level level;
        private Logger parent;
        private bool useParentHandlers;

        static Logger()
        {
            Handler handler = new ConsoleHandler();
            handler.Level = Level.ALL;
            Global.Level = Level.ALL;
            Global.AddHandler(handler);
        }

        protected Logger(string name, string resourceBundleName)
        {
            this.name = name;
            this.resourceBundleName = resourceBundleName;
            this.handlers = new List<Handler>();
            this.useParentHandlers = true;
        }

        public string Name
        {
            get { return name; }
        }

        public Level Level
        {
            get { return level; }
            set { level = value; }
        }

        public Handler[] Handlers
        {
            get { return handlers.ToArray(); }
        }

        public Logger Parent
        {
            get { return parent; }
            set { parent = value; }
        }

        public bool UseParentHandlers
        {
            get { return useParentHandlers; }
            set { useParentHandlers = value; }
        }

        public Filter Filter
        {
            get { return filter; }
            set { filter = value; }
        }

        public void AddHandler(Handler handler)
        {
            handlers.Add(handler);
        }

        public void RemoveHandler(Handler handler)
        {
            handlers.Remove(handler);
        }

        public void Entering(string sourceClass, string sourceMethod)
        {
            LogRecord record = new LogRecord(Level.FINER, "ENTRY");
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void Entering(string sourceClass, string sourceMethod, object param)
        {
            LogRecord record = new LogRecord(Level.FINER, "ENTRY " + param.ToString());
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            object[] parameters = new object[1];
            parameters[0] = param;
            record.Parameters = parameters;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void Entering(string sourceClass, string sourceMethod, object[] param)
        {
            string message = "ENTRY";
            foreach(object obj in param) 
            {
                message += " " + obj.ToString();
            }
            LogRecord record = new LogRecord(Level.FINER, message);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.Parameters = param;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void Exiting(string sourceClass, string sourceMethod)
        {
            LogRecord record = new LogRecord(Level.FINER, "RETURN");
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void Exiting(string sourceClass, string sourceMethod, object result)
        {
            LogRecord record = new LogRecord(Level.FINER, "RETURN " + result);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            object[] parameters = new object[1];
            parameters[0] = result;
            record.Parameters = parameters;
            Log(record);
        }

        public void Log(LogRecord record)
        {
            if (!IsLoggable(record.Level)) return;
            if (filter != null && !filter.IsLoggable(record)) return;
            foreach (Handler h in handlers)
            {
                h.Publish(record);
            }
            if (parent != null && useParentHandlers)
            {
                parent.Log(record);
            }
        }

        public void Log(Level level, string message)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void Log(Level level, string message, object obj)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void Log(Level level, string message, object[] objs)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void Log(Level level, string message, Exception exception)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void LogP(Level level, string sourceClass, string sourceMethod, string message)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void LogP(Level level, string sourceClass, string sourceMethod, string message, object param)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            record.Parameters = new object[1];
            record.Parameters[0] = param;
            Log(record);
        }

        public void LogP(Level level, string sourceClass, string sourceMethod, string message, object[] param)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            record.Parameters = param;
            Log(record);
        }

        public void LogP(Level level, string sourceClass, string sourceMethod, string message, Exception exception)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            record.Exception = exception;
            Log(record);
        }

        public void LogRB(Level level, string sourceClass, string sourceMethod, string bundleName, string message)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.RessourceBundleName = bundleName;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }

        public void LogRB(Level level, string sourceClass, string sourceMethod, string bundleName, string message, object param)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.RessourceBundleName = bundleName;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            record.Parameters = new object[1];
            record.Parameters[0] = param;
            Log(record);
        }

        public void LogRB(Level level, string sourceClass, string sourceMethod, string bundleName, string message, object[] param)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.RessourceBundleName = bundleName;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            record.Parameters = param;
            Log(record);
        }

        public void LogRB(Level level, string sourceClass, string sourceMethod, string bundleName, string message, Exception exception)
        {
            LogRecord record = new LogRecord(level, message);
            record.LoggerName = name;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.RessourceBundleName = bundleName;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            record.Exception = exception;
            Log(record);
        }

        public bool IsLoggable(Level level)
        {
            return this.level == null || this.level.Value <= level.Value;
        }

        public void Finest(string message)
        {
            Log(Level.FINEST, message);
        }

        public void Finer(string message)
        {
            Log(Level.FINER, message);
        }

        public void Fine(string message)
        {
            Log(Level.FINE, message);
        }

        public void Info(string message)
        {
            Log(Level.INFO, message);
        }

        public void Config(string message)
        {
            Log(Level.CONFIG, message);
        }

        public void Warning(string message)
        {
            Log(Level.WARNING, message);
        }

        public void Severe(string message)
        {
            Log(Level.SEVERE, message);
        }

        public void Throwing(string sourceClass, string sourceMethod, Exception exception)
        {
            LogRecord record = new LogRecord(Level.FINER, "THROW");
            record.LoggerName = name;
            record.Exception = exception;
            record.SourceClassName = sourceClass;
            record.SourceMethodName = sourceMethod;
            record.ThreadID = Thread.CurrentThread.ManagedThreadId;
            Log(record);
        }


        public static Logger GetLogger(string name)
        {
            char[] spe = { '.' };
            string[] pack = name.Split(spe);
            string current = "";
            Logger parent = Logger.Global;
            for (int c = 0; c < pack.Length - 1; c++)
            {
                if (current != "") current += ".";
                current += pack[c];
                Logger log = LogManager.GetInstance().GetLogger(current);
                if (log != null)
                {
                    parent = log;
                }
                else break;
            }
            Logger logger = LogManager.GetInstance().GetLogger(name);
            if (logger == null)
            {
                logger = new Logger(name, "");
                LogManager.GetInstance().AddLogger(logger);
            }
            logger.Parent = parent;
            return logger;
        }
    }
}
